#include "bglProgram.h"

#include <iostream>
#include <assert.h>


static const GLchar* ReadShader(const char* filename)
{
	FILE* infile;
	infile = fopen(filename, "rb");

	if (!infile) {
		std::cerr << "Unable to open file '" << filename << "'" << std::endl;
		return NULL;
	}

	fseek(infile, 0, SEEK_END);
	int len = ftell(infile);
	fseek(infile, 0, SEEK_SET);

	GLchar* source = new GLchar[len + 1];

	fread(source, 1, len, infile);
	fclose(infile);

	source[len] = 0;

	return const_cast<const GLchar*>(source);
}

GLuint BGLProgram::createShader(GLenum shaderType, const char* source)
{
	GLuint ShaderObj = glCreateShader(shaderType);
	glShaderSource(ShaderObj, 1, &source, NULL);
	glCompileShader(ShaderObj);  // 锟斤拷锟叫憋拷锟斤拷

	GLint   status = 0;
	char    compileLog[1024] = { 0 };
	glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE)
	{
		glGetShaderInfoLog(ShaderObj, sizeof(compileLog), 0, compileLog);
		std::cerr << "ERROR::SHADER::PROGRAM::CREATE_FAILED" << compileLog << std::endl;
		assert(false && compileLog);
	}

	return ShaderObj;
}

void BGLProgram::compileShader(const GLchar* vertex_shader, const GLchar* fragment_shader, const GLchar* geometry_shader)
{
	GLuint m_vShader = createShader(GL_VERTEX_SHADER, vertex_shader);
	GLuint m_fShader = createShader(GL_FRAGMENT_SHADER, fragment_shader);
	
	m_id = glCreateProgram();  // 锟斤拷锟斤拷锟秸碉拷锟斤拷色锟斤拷锟斤拷锟斤拷*/
	glAttachShader(m_id, m_vShader);
	glAttachShader(m_id, m_fShader);
	if (geometry_shader != nullptr)
	{
		GLuint m_gShader = createShader(GL_GEOMETRY_SHADER, geometry_shader);
		glAttachShader(m_id, m_gShader);
	}
 	glLinkProgram(m_id);

	int success;
	char infoLog[512];
	glGetProgramiv(m_id, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(m_id, 512, NULL, infoLog);
		std::cerr << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
	}
	glDeleteShader(m_vShader);
	glDeleteShader(m_fShader);
}

void BGLProgram::createProgramFromStr(const GLchar* vertex_shader, const GLchar* fragment_shader, const GLchar* geometry_shader)
{
	compileShader(vertex_shader, fragment_shader, geometry_shader);
}

void BGLProgram::createProgramFromFile(const char* vertex_shader_file, const char* fragment_shader_file, const GLchar* geometry_shader)
{
	const GLchar* vertex_shader = ReadShader(vertex_shader_file);
	const GLchar* fragment_shader = ReadShader(fragment_shader_file);
	compileShader(vertex_shader, fragment_shader, geometry_shader);
}

void BGLProgram::begin()
{
	glUseProgram(m_id);
}

void BGLProgram::end()
{
	glUseProgram(0);
}

GLuint BGLProgram::getId()
{
	return m_id;
}
